package com.zidongxiangxi.lock.starter.aop;

import com.zidongxiangxi.lock.core.DistributedLock;
import com.zidongxiangxi.lock.core.DistributedLockFactory;
import com.zidongxiangxi.lock.starter.annotation.Locking;
import com.zidongxiangxi.lock.starter.exception.IllegalLockAnnotationException;
import com.zidongxiangxi.lock.starter.exception.LockException;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 基于aop的分布式锁实现，解决在JDBC事务提交后再释放，而且不需要所有开发在代码写死锁的逻辑
 *
 * @author chenxudong chenxudong
 * @version 1.0.0
 */
@Slf4j
public class DistributedLockInterceptor implements MethodInterceptor {
    private static final Byte DEFAULT_MAP_VALUE = 1;

    /**
     * 目标方法接口上的锁注解map
     * key = 目标方法， value = 目标方法接口上的注解
     */
    private final Map<Method, Locking> interfaceLockAnnotationMap = new ConcurrentHashMap<>(16);
    private final Map<Method, Byte> interfaceLockAnnotationMissingMap = new ConcurrentHashMap<>(16);
    private final DistributedLockFactory distributedLockFactory;
    public DistributedLockInterceptor(DistributedLockFactory distributedLockFactory) {
        this.distributedLockFactory = distributedLockFactory;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Locking locking = getLockAnnotation(invocation);
        if (Objects.isNull(locking)) {
            return invocation.proceed();
        }
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new MethodBasedEvaluationContext(invocation.getThis(),
            invocation.getMethod(), invocation.getArguments(), new DefaultParameterNameDiscoverer());

        if (!need2Lock(locking, parser, context)) {
            return invocation.proceed();
        }

        String lockName = generateLockName(locking, parser, context);
        DistributedLock lock = distributedLockFactory.createLock(lockName);
        try {
            if (locking.waitTime() > 0) {
                boolean success = lock.tryLock(locking.waitTime(), TimeUnit.MILLISECONDS);
                if (!success) {
                    throw new LockException(locking.failMsg());
                }
            } else {
                lock.lock();
            }
            return invocation.proceed();
        } finally {
            if (lock != null) {
                lock.unlock();
            }
            lock = null;
        }
    }

    /**
     * 根据被拦截的参数生成一个锁定义
     */
    private String generateLockName(Locking locking, ExpressionParser parser, StandardEvaluationContext context) {
        // 执行 SpEL，获取 lock id 的值
        String lockId = parser.parseExpression(locking.id()).getValue(context, String.class);
        if (StringUtils.isEmpty(lockId)) {
            throw new LockException(String.format("不能创建锁，获取不到要指定参数为'%s'的锁ID值", locking.id()));
        }
        return lockId;
    }

    private boolean need2Lock(Locking locking, ExpressionParser parser, StandardEvaluationContext context) {
        if (StringUtils.isEmpty(locking.condition())) {
            return true;
        }
        //condition为空或为true时加锁
        Boolean conditionValue = parser.parseExpression(locking.condition()).getValue(context, Boolean.class);
        return Objects.equals(conditionValue, true);
    }

    private Locking getLockAnnotation(MethodInvocation invocation) {
        Locking lockAnnotation;
        // 先从目标实现方法获取注解
        Method targetMethod = invocation.getMethod();
        lockAnnotation = targetMethod.getAnnotation(Locking.class);
        if (Objects.nonNull(lockAnnotation)) {
            return lockAnnotation;
        }
        // 再从缓存中获取目标方法接口上的注解
        lockAnnotation = interfaceLockAnnotationMap.get(targetMethod);
        if (Objects.nonNull(lockAnnotation)) {
            return lockAnnotation;
        }
        // 如果曾经找过，且找不到，则直接返回
        if (interfaceLockAnnotationMissingMap.containsKey(targetMethod)) {
            return null;
        }

        // 如果都没有的话，最后从目标方法接口上的获取注解，并放入缓存中
        Class<?> targetClass = invocation.getThis().getClass();
        Class<?>[] interfaces = targetClass.getInterfaces();

        List<Locking> interfaceAnnotations = new ArrayList<>();
        for (Class<?> anInterface : interfaces) {
            try {
                Method interfaceMethod = anInterface.getMethod(targetMethod.getName(), targetMethod.getParameterTypes());
                Locking interfaceLockAnnotation = interfaceMethod.getDeclaredAnnotation(Locking.class);
                if (Objects.nonNull(interfaceLockAnnotation)) {
                    interfaceAnnotations.add(interfaceLockAnnotation);
                }
            } catch (Exception ignore) {
                // do nothing
            }
        }
        if (CollectionUtils.isEmpty(interfaceAnnotations)) {
            interfaceLockAnnotationMissingMap.put(targetMethod, DEFAULT_MAP_VALUE);
            return null;
        }
        if (interfaceAnnotations.size() > 1) {
            throw new IllegalLockAnnotationException("继承的接口上的分布式锁注解大于1");
        }
        lockAnnotation = interfaceAnnotations.get(0);
        interfaceLockAnnotationMap.put(targetMethod, lockAnnotation);
        return lockAnnotation;
    }

}
